<template>
	<div class="upload-container" :class="{ 'multi-upload': props.multi }">
		<div class="image-list">
			<div v-for="(file, index) in files" :key="file.FileId || index" class="image-item" :class="{ 'is-uploading': file.status === 'uploading' }">
				<div class="image-wrapper">
					<div class="img-container">
						<img v-if="file.url && !file.hasError" :src="file.url" :alt="file.name" @load="onImageLoad(file)" @error="onImageError(file)" />
						<div v-else class="image-placeholder" :class="{ 'error-placeholder': file.hasError }">
							<i class="fa fa-image"></i>
							<span v-if="file.hasError">图片加载失败</span>
							<span v-else-if="file.status === 'loading'">加载中...</span>
							<span v-else>暂无图片</span>
						</div>
					</div>
					<div class="item-mask">
						<div class="action-icons">
							<div v-if="file.url" class="action-btn preview-btn" @click.stop="onPreview(file)">
								<el-icon><ZoomIn /></el-icon>
							</div>
							<div v-if="!props.disabled" class="action-btn delete-btn" @click.stop="handleRemove(file)">
								<el-icon><Delete /></el-icon>
							</div>
						</div>
					</div>
					<div v-if="file.status === 'uploading' || file.status === 'loading'" class="upload-progress-mask">
						<div class="progress-content">
							<div class="loading-spinner">
								<i class="fa fa-spinner fa-spin"></i>
							</div>
							<span class="progress-text">
								{{ file.status === 'uploading' ? '上传中...' : '加载中...' }}
							</span>
						</div>
					</div>
					<div v-if="props.multi && files.length > 1" class="corner-mark">{{ index + 1 }}</div>
				</div>
			</div>

			<!-- 上传按钮 -->
			<el-upload
				v-if="!props.disabled && (files?.length === 0 || (props.multi && files.length < props.limit))"
				ref="uploadRef"
				:action="'/api/_file/Upload'"
				:on-success="onSuccess"
				:on-progress="onProgress"
				:on-error="onError"
				:before-upload="beforeUpload"
				:on-exceed="onExceed"
				:headers="header"
				:file-list="[]"
				:show-file-list="false"
				:disabled="props.disabled"
				:multiple="props.multi"
				:accept="'image/*'"
				:limit="props.limit"
				:auto-upload="true"
				class="upload-trigger"
			>
				<div class="upload-btn">
					<i class="fa fa-plus"></i>
					<span v-if="props.multi" class="upload-text">{{ files?.length || 0 }}/{{ props.limit }}</span>
				</div>
			</el-upload>

			<!-- 禁用状态下的占位符 -->
			<div v-if="props.disabled && files?.length === 0" class="upload-placeholder">
				<div class="placeholder-content">
					<i class="fa fa-image"></i>
					<span>暂无图片</span>
				</div>
			</div>
		</div>

		<!-- 图片预览组件 -->
		<el-dialog v-model="previewOpen" append-to-body width="800px">
			<div class="preview-container">
				<img :src="previewUrl" alt="Preview" />
			</div>
		</el-dialog>
	</div>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { Local } from '/@/utils/storage';
import fileApi from '/@/api/file';
import { ElDialog, ElMessage, ElMessageBox } from 'element-plus';
import { ZoomIn, Delete } from '@element-plus/icons-vue';

const emit = defineEmits(['refresh', 'update:modelValue', 'update:deletedFiles', 'upload-status']);
const uploadRef = ref();
const files = ref<any[]>([]);
const isUpdatingFromUpload = ref(false);

// 定义父组件传过来的值
const props = defineProps({
	multi: Boolean,
	disabled: Boolean,
	modelValue: null,
	deletedFiles: null,
	maxSize: { type: Number, default: 5 }, // 最大文件大小(MB)
	limit: { type: Number, default: 1 }, // 添加文件数量限制，默认为1
});

const fileValue = computed({
	get() {
		return props.modelValue;
	},
	set(value) {
		emit('update:modelValue', value);
	},
});

const deletedFiles = computed<any[]>({
	get() {
		return props.deletedFiles ?? [];
	},
	set(value) {
		emit('update:deletedFiles', value);
	},
});

const previewOpen = ref(false);
const previewUrl = ref('');

const header = computed(() => {
	return { Authorization: `Bearer ${Local.get('token')}` };
});

// 计算是否有文件正在上传
const isUploading = computed(() => {
	return files.value.some((file) => file.status === 'uploading' || file.status === 'loading');
});

// 监听上传状态变化并发出事件
watch(
	isUploading,
	(newStatus) => {
		emit('upload-status', newStatus);
	},
	{ immediate: true }
);

// 监听文件值变化
watch(
	fileValue,
	async () => {
		// 防止上传过程中的循环更新
		if (isUpdatingFromUpload.value) {
			return;
		}

		if (!fileValue.value) {
			files.value = [];
			return;
		}

		if (!files.value) {
			files.value = [];
		}

		if (!props.multi) {
			let fv = files.value && files.value.length > 0 ? files.value[0].FileId : null;
			if (props.modelValue !== fv) {
				files.value = [];
				if (props.modelValue) {
					try {
						let filename = await fileApi().getName(props.modelValue);
						files.value = [
							{
								name: filename,
								url: `/api/_file/getfile/${props.modelValue}?width=150&height=150`,
								FileId: props.modelValue,
							},
						];
					} catch (error) {
						console.error('图片组件 - 加载图片失败:', error);
					}
				}
			}
		} else {
			// 多图模式：添加严格的空值检查
			if (!props.modelValue || !Array.isArray(props.modelValue) || props.modelValue.length === 0) {
				files.value = [];
				return;
			}

			// 创建新的files数组，避免与现有上传中的文件冲突
			const newFiles = [];

			for (let i = 0; i < props.modelValue.length; i++) {
				// 检查当前项是否存在且有FileId
				const item = props.modelValue[i];
				if (!item || !item.FileId) {
					continue;
				}

				// 确保FileId是字符串类型，不是数组或其他类型
				const fileId = typeof item.FileId === 'string' ? item.FileId : String(item.FileId);
				if (!fileId || fileId === 'undefined' || fileId === 'null') {
					continue;
				}

				// 检查是否已经在files中存在（通过FileId匹配）
				const existingFile = files.value.find((f) => f.FileId === fileId);

				if (existingFile) {
					// 如果已存在，直接使用现有的文件对象
					newFiles.push(existingFile);
				} else {
					// 如果不存在，创建新的文件对象
					try {
						let filename = await fileApi().getName(fileId);
						let nv = {
							name: filename,
							url: `/api/_file/getfile/${fileId}?width=150&height=150`,
							FileId: fileId,
							keyID: item.ID,
							Order: i,
							status: 'success',
						};
						newFiles.push(nv);
					} catch (error) {
						console.error('获取文件名失败:', error, '文件ID:', fileId);
						// 如果获取文件失败，创建一个错误状态的占位符
						let nv = {
							name: '未知文件',
							url: '',
							FileId: fileId,
							keyID: item.ID,
							Order: i,
							status: 'error',
							hasError: true,
						};
						newFiles.push(nv);
					}
				}
			}

			// 更新files数组
			files.value = newFiles;
		}
	},
	{ immediate: true }
);

/**
 * 预览图片
 * @param file 文件对象，包含FileId
 */
const onPreview = (file: any) => {
	if (file.FileId) {
		previewUrl.value = `/api/_file/getfile/${file.FileId}`; // 使用原始尺寸的图片URL
	} else {
		previewUrl.value = file.url; // 上传中的文件使用本地URL
	}
	previewOpen.value = true;
};

/**
 * 上传前验证
 * @param file 待上传的文件
 * @returns boolean 是否通过验证
 */
const beforeUpload = (file: File) => {
	const isImage = file.type.startsWith('image/');
	const isLt5M = file.size / 1024 / 1024 < props.maxSize;

	if (!isImage) {
		ElMessage.error('只能上传图片文件！');
		return false;
	}
	if (!isLt5M) {
		ElMessage.error(`图片大小不能超过 ${props.maxSize}MB！`);
		return false;
	}

	// 检查当前文件数量
	if (!props.multi && files.value && files.value.length >= props.limit) {
		ElMessage.warning(`最多只能上传 ${props.limit} 张图片`);
		return false;
	}

	// 多图模式下检查文件数量限制
	if (props.multi && files.value && files.value.length >= props.limit) {
		ElMessage.warning(`最多只能上传 ${props.limit} 张图片`);
		return false;
	}

	return true;
};

/**
 * 上传进度回调
 * @param event 进度事件
 * @param file 上传的文件
 */
const onProgress = (event: any, file: any) => {
	file.percentage = Math.round((event.loaded * 100) / event.total);
	file.status = 'uploading';

	// 多图模式下，为正在上传的文件添加临时显示项
	if (props.multi && file.raw) {
		const existingIndex = files.value.findIndex((f) => f.uid === file.uid);
		const uploadingFile = {
			name: file.name,
			url: URL.createObjectURL(file.raw), // 使用本地预览URL
			FileId: null,
			status: 'uploading',
			uid: file.uid,
			percentage: file.percentage,
		};

		if (existingIndex === -1) {
			files.value.push(uploadingFile);
		} else {
			files.value[existingIndex] = uploadingFile;
		}
	}
};

/**
 * 上传成功回调
 * @param res 上传响应结果
 * @param uploadFile 当前上传的文件
 */
const onSuccess = (res: any, uploadFile: any) => {
	uploadFile.FileId = res.Id;
	uploadFile.url = `/api/_file/getfile/${res.Id}?width=150&height=150`;
	uploadFile.status = 'loading'; // 设置为加载状态，等待图片load事件

	if (!props.multi) {
		fileValue.value = res.Id;
		if (files.value.length > 0) {
			deletedFiles.value.push(files.value[0].FileId);
			files.value = [];
		}
		files.value.push({
			name: uploadFile.name,
			url: uploadFile.url,
			FileId: res.Id,
			status: 'loading',
		});
	} else {
		// 多图模式：替换或添加上传成功的文件
		const newFile = {
			name: uploadFile.name,
			url: uploadFile.url,
			FileId: res.Id,
			status: 'loading',
			uid: uploadFile.uid,
		};

		// 查找是否有对应的上传中文件（通过uid匹配）
		const uploadingIndex = files.value.findIndex((f) => f.uid === uploadFile.uid);
		if (uploadingIndex !== -1) {
			// 替换上传中的文件
			files.value[uploadingIndex] = newFile;
		} else {
			// 如果没找到对应的上传中文件，检查是否重复
			const existingIndex = files.value.findIndex((f) => f.FileId === res.Id);
			if (existingIndex === -1) {
				files.value.push(newFile);
			} else {
				files.value[existingIndex] = newFile;
			}
		}

		// 更新modelValue，包含所有有效的文件
		const validFiles = files.value.filter((f) => f.FileId && (f.status === 'success' || f.status === 'loading'));
		const newValue = validFiles.map((item, i) => ({ FileId: item.FileId, Order: i }));

		// 设置标志位，防止watch中的循环更新
		isUpdatingFromUpload.value = true;
		fileValue.value = newValue;
		setTimeout(() => {
			isUpdatingFromUpload.value = false;
		}, 100);
	}
};

/**
 * 移除文件
 * @param file 要移除的文件对象
 */
const onRemove = (file: any) => {
	// 只有已上传成功的文件才需要调用删除API
	if (file.FileId && file.status !== 'uploading') {
		fileApi().deleteFile(file.FileId);
	}

	if (!props.multi) {
		if (file.FileId) {
			deletedFiles.value.push(file.FileId);
		}
		files.value = [];
		fileValue.value = null;
	} else {
		const index = files.value.findIndex((item) => (item.FileId ? item.FileId === file.FileId : item.uid === file.uid));
		if (index > -1) {
			if (file.FileId) {
				deletedFiles.value.push(file.FileId);
			}
			files.value.splice(index, 1);

			// 更新fileValue，只包含有FileId的文件
			const validFiles = files.value.filter((f) => f.FileId);
			fileValue.value = validFiles.map((item, i) => ({
				FileId: item.FileId,
				ID: item.keyID,
				Order: i,
			}));
		}
	}
};

/**
 * 上传错误回调
 * @param error 错误信息
 * @param file 上传的文件
 */
const onError = (error: any, file: any) => {
	ElMessage.error(`${file.name} 上传失败：${error.message || '未知错误'}`);

	// 多图模式下移除失败的上传项
	if (props.multi && file.uid) {
		const failedIndex = files.value.findIndex((f) => f.uid === file.uid);
		if (failedIndex !== -1) {
			files.value.splice(failedIndex, 1);
		}
	}
};

/**
 * 删除文件确认
 * @param file 要删除的文件对象
 */
const handleRemove = (file: any) => {
	// 如果是上传中的文件，直接删除不需要确认
	if (file.status === 'uploading') {
		onRemove(file);
		return;
	}

	ElMessageBox.confirm('确定要删除这张图片吗？', '提示', {
		confirmButtonText: '确定',
		cancelButtonText: '取消',
		type: 'warning',
	})
		.then(() => {
			onRemove(file);
			ElMessage.success('删除成功');
		})
		.catch(() => {});
};

/**
 * 超出上传限制处理
 */
const onExceed = () => {
	ElMessage.warning(`最多只能上传 ${props.limit} 张图片`);
};

/**
 * 图片加载完成回调
 * @param file 文件对象
 */
const onImageLoad = (file: any) => {
	if (file.status === 'loading') {
		file.status = 'success';

		// 多图模式下图片加载完成后更新fileValue
		if (props.multi) {
			const validFiles = files.value.filter((f) => f.FileId && (f.status === 'success' || f.status === 'loading'));
			const newValue = validFiles.map((item, i) => ({ FileId: item.FileId, Order: i }));

			// 设置标志位，防止watch中的循环更新
			isUpdatingFromUpload.value = true;
			fileValue.value = newValue;
			setTimeout(() => {
				isUpdatingFromUpload.value = false;
			}, 100);
		}
	}
};

/**
 * 图片加载失败回调
 * @param file 文件对象
 */
const onImageError = (file: any) => {
	// 静默处理图片加载失败，不显示错误消息
	console.warn('图片加载失败，将显示占位符:', file?.FileId);

	// 设置加载失败状态，让模板显示占位符
	if (file) {
		file.status = 'error';
		file.hasError = true;
	}
};

// 暴露变量
defineExpose({});
</script>

<style lang="scss" scoped>
.upload-container {
	width: 100%;
}

.image-list {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	align-items: flex-start;

	// 响应式布局优化
	@media (max-width: 768px) {
		gap: 6px;
	}
}

.image-item {
	position: relative;
	width: 104px;
	height: 104px;
	border-radius: 4px;
	overflow: hidden;
	background: #f5f7fa;
	border: 1px solid #e4e7ed;
	flex-shrink: 0; // 防止图片被压缩

	// 多图模式下稍微调整大小
	.multi-upload & {
		width: 88px;
		height: 88px;

		@media (max-width: 768px) {
			width: 76px;
			height: 76px;
		}
	}

	.image-wrapper {
		width: 100%;
		height: 100%;
		position: relative;
		display: flex;
		align-items: center;
		justify-content: center;
		overflow: hidden;

		.img-container {
			width: 100%;
			height: 100%;
			display: flex;
			align-items: center;
			justify-content: center;
			overflow: hidden;

			img {
				max-width: 100%;
				max-height: 100%;
				object-fit: cover;
			}

			.image-placeholder {
				display: flex;
				flex-direction: column;
				align-items: center;
				justify-content: center;
				width: 100%;
				height: 100%;
				color: var(--el-text-color-placeholder);
				background-color: var(--el-fill-color-lighter);

				i {
					font-size: 24px;
					margin-bottom: 4px;
				}

				span {
					font-size: 12px;
					text-align: center;
					padding: 0 4px;
				}

				&.error-placeholder {
					background-color: var(--el-color-error-light-9);
					color: var(--el-color-error);
					border: 1px dashed var(--el-color-error-light-5);

					i {
						color: var(--el-color-error-light-3);
					}
				}
			}
		}
	}

	&.is-uploading {
		.image-wrapper {
			opacity: 0.8;
		}
	}
}

.upload-progress-mask {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(255, 255, 255, 0.95);
	backdrop-filter: blur(2px);
	display: flex;
	align-items: center;
	justify-content: center;
	flex-direction: column;
	border-radius: 4px;
	animation: fadeIn 0.3s ease-out;

	.progress-content {
		text-align: center;

		.loading-spinner {
			display: flex;
			align-items: center;
			justify-content: center;
			width: 50px;
			height: 50px;

			i {
				font-size: 24px;
				color: #409eff;
			}
		}

		.progress-text {
			display: block;
			margin-top: 8px;
			font-size: 12px;
			color: var(--el-color-primary);
		}
	}
}

.upload-trigger {
	:deep(.el-upload) {
		width: 104px;
		height: 104px;
		flex-shrink: 0; // 防止被压缩
	}

	// 多图模式下调整上传按钮大小
	.multi-upload & {
		:deep(.el-upload) {
			width: 88px;
			height: 88px;

			@media (max-width: 768px) {
				width: 76px;
				height: 76px;
			}
		}
	}

	.upload-btn {
		width: 100%;
		height: 100%;
		border: 1px dashed var(--el-border-color);
		border-radius: 4px;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		cursor: pointer;
		transition: all 0.2s;

		&:hover {
			border-color: var(--el-color-primary);
			background: var(--el-color-primary-light-9);
		}

		i {
			font-size: 24px;
			color: var(--el-text-color-secondary);
			margin-bottom: 4px;
		}

		.upload-text {
			font-size: 12px;
			color: var(--el-text-color-secondary);
		}
	}
}

.upload-placeholder {
	width: 104px;
	height: 104px;
	border: 1px dashed var(--el-border-color-light);
	border-radius: 4px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--el-fill-color-lighter);

	.placeholder-content {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		color: var(--el-text-color-placeholder);

		i {
			font-size: 24px;
			margin-bottom: 4px;
		}

		span {
			font-size: 12px;
		}
	}
}

.item-mask {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: rgba(0, 0, 0, 0.5);
	opacity: 0;
	transition: opacity 0.2s ease;
	z-index: 2;

	&:hover {
		opacity: 1;
	}

	.action-icons {
		display: flex;
		gap: 12px;

		.action-btn {
			width: 32px;
			height: 32px;
			display: flex;
			align-items: center;
			justify-content: center;
			color: #fff;
			font-size: 16px;
			cursor: pointer;
			border-radius: 4px;
			background: rgba(255, 255, 255, 0.2);
			transition: background 0.2s ease;

			&:hover {
				background: rgba(255, 255, 255, 0.3);
			}
		}
	}
}

.corner-mark {
	position: absolute;
	top: 4px;
	right: 4px;
	background: rgba(0, 0, 0, 0.5);
	color: #fff;
	font-size: 12px;
	padding: 2px 6px;
	border-radius: 2px;
	z-index: 1;
}

.preview-container {
	width: 100%;
	display: flex;
	justify-content: center;
	align-items: center;

	img {
		max-width: 100%;
		max-height: 70vh;
		object-fit: contain;
	}
}

// 淡入动画
@keyframes fadeIn {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}
</style>
